
/**
  ******************************************************************************
  * Copyright 2021 The Microbee Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       battery_monitor.c
  * @author     baiyang
  * @date       2022-10-23
  ******************************************************************************
  */

/*----------------------------------include-----------------------------------*/
#include "battery_monitor.h"

#include "battery_monitor_analog.h"

#include <assert.h>

#include <uITC/uITC.h>
#include <uITC/uITC_msg.h>

#include <notify/notify.h>
#include <parameter/param.h>
#include <gcs_mavlink/gcs.h>
#include <common/time/gp_time.h>
#include <vehicle/vehicle_type.h>
#include <board_config/board_config.h>
/*-----------------------------------macro------------------------------------*/

/*----------------------------------typedef-----------------------------------*/

/*---------------------------------prototype----------------------------------*/
static void battmonitor_publish_state(uint8_t instance);
static void battmonitor_publish_status();
/*----------------------------------variable----------------------------------*/
struct battery_monitor batt_monitor;

static uitc_battery_state battery_state;
static uitc_battery_status battery_status;

#if CONFIG_HAL_BOARD != HAL_BOARD_SITL_WIN
_Static_assert(MB_BATT_MONITOR_MAX_INSTANCES <= 16, "More batteries are enabled then can be reset");
#else
static_assert(MB_BATT_MONITOR_MAX_INSTANCES <= 16, "More batteries are enabled then can be reset");
#endif
/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
battery_monitor_t battmonitor_get_singleton()
{
    return &batt_monitor;
}

// Default constructor.
// Note that the Vector/Matrix constructors already implicitly zero
// their values.
//
void battmonitor_ctor(uint32_t log_battery_bit, battery_failsafe_handler_fn_t battery_failsafe_handler_fn, const int8_t *failsafe_priorities)
{
    battery_monitor_assign_param();

    batt_monitor._log_battery_bit = log_battery_bit;
    batt_monitor._battery_failsafe_handler_fn = battery_failsafe_handler_fn;
    batt_monitor._failsafe_priorities = failsafe_priorities;

}

// detect and initialise any available battery monitors
void battmonitor_init()
{
    // check init has not been called before
    if (batt_monitor._num_instances != 0) {
        return;
    }

    batt_monitor._highest_failsafe_priority = INT8_MAX;

#if defined(HAL_BATT_MONITOR_DEFAULT)
    batt_monitor._params[0]._type = HAL_BATT_MONITOR_DEFAULT;
#endif

    // create each instance
    for (uint8_t instance=0; instance<MB_BATT_MONITOR_MAX_INSTANCES; instance++) {
        // clear out the cell voltages
        rt_memset(&batt_monitor.state[instance].cell_voltages, 0xFF, sizeof(struct BattMonitor_Cells));

        switch (battmonitor_get_type2(instance)) {
            case BATT_MONITOR_ANALOG_VOLTAGE_ONLY:
            case BATT_MONITOR_ANALOG_VOLTAGE_AND_CURRENT:
                batt_monitor.drivers[instance] = battery_monitor_analog_probe(&batt_monitor.state[instance], &batt_monitor._params[instance]);
                break;
            case BATT_MONITOR_NONE:
            default:
                break;
        }

        // call init function for each backend
        if (batt_monitor.drivers[instance] != NULL) {
            battmonitor_backend_init(batt_monitor.drivers[instance]);
            // _num_instances is actually the index for looping over instances
            // the user may have BATT_MONITOR=0 and BATT2_MONITOR=7, in which case
            // there will be a gap, but as we always check for drivers[instances] being nullptr
            // this is safe
            batt_monitor._num_instances = instance + 1;
        }
    }
}

/// Read the battery voltage and current for all batteries.  Should be called at 10hz
void battmonitor_read()
{
#if defined(HAL_LOGGING_ENABLED)
    AP_Logger *logger = AP_Logger::get_singleton();
    if (logger != nullptr && logger->should_log(_log_battery_bit)) {
        logger->Write_Power();
    }
#endif

    for (uint8_t i=0; i<batt_monitor._num_instances; i++) {
        if (batt_monitor.drivers[i] != NULL && battmonitor_get_type2(i) != BATT_MONITOR_NONE) {
            battmonitor_backend_read(batt_monitor.drivers[i]);
            battmonitor_backend_update_resistance_estimate(batt_monitor.drivers[i]);

#if defined(HAL_LOGGING_ENABLED)
            if (logger != nullptr && logger->should_log(_log_battery_bit)) {
                const uint64_t time_us = time_micros64();
                battmonitor_backend_log_write_bat(batt_monitor.drivers[i], i, time_us);
                battmonitor_backend_log_write_bcl(batt_monitor.drivers[i], i, time_us);
            }
#endif
            battmonitor_publish_state(i);
        }
    }

    battmonitor_check_failsafes();
    battmonitor_checkPoweringOff();

    battmonitor_publish_status();
}

/// get_type - returns battery monitor type
enum BattMonitorType battmonitor_get_type()
{
    return battmonitor_get_type2(MB_BATT_PRIMARY_INSTANCE);
}

enum BattMonitorType battmonitor_get_type2(uint8_t instance)
{
    return (enum BattMonitorType)batt_monitor._params[instance]._type;
}

void battmonitor_check_failsafes(void)
{
    if (brd_get_soft_armed()) {
        for (uint8_t i = 0; i < batt_monitor._num_instances; i++) {
            if (batt_monitor.drivers[i] == NULL) {
                continue;
            }

            const enum BattMonitorFailsafe type = battmonitor_backend_update_failsafes(batt_monitor.drivers[i]);
            if (type <= batt_monitor.state[i].failsafe) {
                continue;
            }

            int8_t action = 0;
            const char *type_str = NULL;
            switch (type) {
                case BattMonitorFailsafeNone:
                    continue; // should not have been called in this case
                case BattMonitorFailsafeLow:
                    action = batt_monitor._params[i]._failsafe_low_action;
                    type_str = "low";
                    break;
                case BattMonitorFailsafeCritical:
                    action = batt_monitor._params[i]._failsafe_critical_action;
                    type_str = "critical";
                    break;
            }

            GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Battery %d is %s %.2fV used %.0f mAh", i + 1, type_str,
                            (double)battmonitor_voltage(i), (double)batt_monitor.state[i].consumed_mah);
            batt_monitor._has_triggered_failsafe = true;

            notify_flags.failsafe_battery = true;
            batt_monitor.state[i].failsafe = type;

            // map the desired failsafe action to a prioritiy level
            int8_t priority = 0;
            if (batt_monitor._failsafe_priorities != NULL) {
                while (batt_monitor._failsafe_priorities[priority] != -1) {
                    if (batt_monitor._failsafe_priorities[priority] == action) {
                        break;
                    }
                    priority++;
                }

            }

            // trigger failsafe if the action was equal or higher priority
            // It's valid to retrigger the same action if a different battery provoked the event
            if (priority <= batt_monitor._highest_failsafe_priority) {
                batt_monitor._battery_failsafe_handler_fn(type_str, action);
                batt_monitor._highest_failsafe_priority = priority;
            }
        }
    }
}

// Check's each smart battery instance for its powering off state and broadcasts notifications
void battmonitor_checkPoweringOff(void)
{
    for (uint8_t i = 0; i < batt_monitor._num_instances; i++) {
        if (batt_monitor.state[i].is_powering_off && !batt_monitor.state[i].powerOffNotified) {
            // Set the AP_Notify flag, which plays the power off tones
            notify_flags.powering_off = true;

            // Send a Mavlink broadcast announcing the shutdown
            mavlink_command_long_t cmd_msg = {0};
            cmd_msg.command = MAV_CMD_POWER_OFF_INITIATED;
            cmd_msg.param1 = i+1;
            gcsmav_send_to_components(MAVLINK_MSG_ID_COMMAND_LONG, (char*)&cmd_msg, sizeof(cmd_msg));
            GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Vehicle %d battery %d is powering off", mavlink_system.sysid, i+1);

            // only send this once
            batt_monitor.state[i].powerOffNotified = true;
        }
    }
}

/*
  check that all configured battery monitors are healthy
 */
bool battmonitor_healthy()
{
    for (uint8_t i=0; i< batt_monitor._num_instances; i++) {
        if (battmonitor_get_type2(i) != BATT_MONITOR_NONE && !battmonitor_healthy2(i)) {
            return false;
        }
    }
    return true;
}

// healthy - returns true if monitor is functioning
bool battmonitor_healthy2(uint8_t instance)
{
    return instance < batt_monitor._num_instances && batt_monitor.state[instance].healthy;
}

/// voltage - returns battery voltage in volts
float battmonitor_voltage(uint8_t instance)
{
    if (instance < batt_monitor._num_instances) {
        return batt_monitor.state[instance].voltage;
    } else {
        return 0.0f;
    }
}

/// get voltage with sag removed (based on battery current draw and resistance)
/// this will always be greater than or equal to the raw voltage
float battmonitor_voltage_resting_estimate(uint8_t instance)
{
    if (instance < batt_monitor._num_instances && batt_monitor.drivers[instance] != NULL) {
        return battmonitor_backend_voltage_resting_estimate(batt_monitor.drivers[instance]);
    } else {
        return 0.0f;
    }
}

/// voltage - returns battery voltage in volts for GCS, may be resting voltage if option enabled
float battmonitor_gcs_voltage(uint8_t instance)
{
    if ((batt_monitor._params[instance]._options & (uint32_t)(BattMonitor_GCS_Resting_Voltage)) != 0) {
        return battmonitor_voltage_resting_estimate(instance);
    }
    if (instance < batt_monitor._num_instances) {
        return batt_monitor.state[instance].voltage;
    } else {
        return 0.0f;
    }
}

/// current_amps - returns the instantaneous current draw in amperes
bool battmonitor_current_amps(float *current, uint8_t instance)
{
    if ((instance < batt_monitor._num_instances) && (batt_monitor.drivers[instance] != NULL) && battmonitor_backend_has_current(batt_monitor.drivers[instance])) {
        *current = batt_monitor.state[instance].current_amps;
        return true;
    } else {
        return false;
    }
}

/// consumed_mah - returns total current drawn since start-up in milliampere.hours
bool battmonitor_consumed_mah(float *mah, const uint8_t instance)
{
    if ((instance < batt_monitor._num_instances) && (batt_monitor.drivers[instance] != NULL) && battmonitor_backend_has_current(batt_monitor.drivers[instance])) {
        *mah = batt_monitor.state[instance].consumed_mah;
        return true;
    } else {
        return false;
    }
}

/// consumed_wh - returns energy consumed since start-up in Watt.hours
bool battmonitor_consumed_wh(float *wh, const uint8_t instance)
{
    if (instance < batt_monitor._num_instances && batt_monitor.drivers[instance] != NULL && battmonitor_backend_has_consumed_energy(batt_monitor.drivers[instance])) {
        *wh = batt_monitor.state[instance].consumed_wh;
        return true;
    } else {
        return false;
    }
}

/// capacity_remaining_pct - returns true if the percentage is valid and writes to percentage argument
bool battmonitor_capacity_remaining_pct(uint8_t *percentage, uint8_t instance)
{
    if (instance < batt_monitor._num_instances && batt_monitor.drivers[instance] != NULL) {
        return battmonitor_backend_capacity_remaining_pct(batt_monitor.drivers[instance], percentage);
    }
    return false;
}

/// time_remaining - returns remaining battery time
bool battmonitor_time_remaining(uint32_t *seconds, uint8_t instance)
{
    if (instance < batt_monitor._num_instances && batt_monitor.drivers[instance] != NULL && batt_monitor.state[instance].has_time_remaining) {
        *seconds = batt_monitor.state[instance].time_remaining;
        return true;
    }
    return false;
}

/// pack_capacity_mah - returns the capacity of the battery pack in mAh when the pack is full
int32_t battmonitor_pack_capacity_mah(uint8_t instance)
{
    if (instance < MB_BATT_MONITOR_MAX_INSTANCES) {
        return batt_monitor._params[instance]._pack_capacity;
    } else {
        return 0;
    }
}

// return true if any battery is pushing too much power
bool battmonitor_overpower_detected()
{
    bool result = false;
    for (uint8_t instance = 0; instance < batt_monitor._num_instances; instance++) {
        result |= battmonitor_overpower_detected2(instance);
    }
    return result;
}

bool battmonitor_overpower_detected2(uint8_t instance)
{
#if MB_BUILD_TYPE(MICROBEE_BUILD_Plane)
    if (instance < batt_monitor._num_instances && batt_monitor._params[instance]._watt_max > 0) {
        float power = batt_monitor.state[instance].current_amps * batt_monitor.state[instance].voltage;
        return batt_monitor.state[instance].healthy && (power > batt_monitor._params[instance]._watt_max);
    }
    return false;
#else
    return false;
#endif
}

bool battmonitor_has_cell_voltages(const uint8_t instance)
{
    if (instance < batt_monitor._num_instances && batt_monitor.drivers[instance] != NULL) {
        return battmonitor_backend_has_cell_voltages(batt_monitor.drivers[instance]);
    }

    return false;
}

// return the current cell voltages, returns the first monitor instances cells if the instance is out of range
const struct BattMonitor_Cells* battmonitor_get_cell_voltages(const uint8_t instance)
{
    if (instance >= MB_BATT_MONITOR_MAX_INSTANCES) {
        return &batt_monitor.state[MB_BATT_PRIMARY_INSTANCE].cell_voltages;
    } else {
        return &batt_monitor.state[instance].cell_voltages;
    }
}

// returns true if there is a temperature reading
bool battmonitor_get_temperature(float *temperature, const uint8_t instance)
{
    if (instance >= MB_BATT_MONITOR_MAX_INSTANCES || batt_monitor.drivers[instance] == NULL) {
        return false;
    } 
    
#if MB_TEMPERATURE_SENSOR_ENABLED
    if (batt_monitor.state[instance].temperature_external_use) {
        *temperature = batt_monitor.state[instance].temperature_external;
        return true;
    }
#endif

    *temperature = batt_monitor.state[instance].temperature;

    return battmonitor_backend_has_temperature(batt_monitor.drivers[instance]);
}

#if MB_TEMPERATURE_SENSOR_ENABLED
// return true when successfully setting a battery temperature from an external source by instance
bool battmonitor_set_temperature(const float temperature, const uint8_t instance)
{
    if (instance >= MB_BATT_MONITOR_MAX_INSTANCES || batt_monitor.drivers[instance] == NULL) {
        return false;
    }
    batt_monitor.state[instance].temperature_external = temperature;
    batt_monitor.state[instance].temperature_external_use = true;
    return true;
}

// return true when successfully setting a battery temperature from an external source by serial_number
bool battmonitor_set_temperature_by_serial_number(const float temperature, const int32_t serial_number)
{
    bool success = false;
    for (uint8_t i = 0; i < batt_monitor._num_instances; i++) {
        if (batt_monitor.drivers[i] != NULL && batt_monitor._params[i]._serial_number == serial_number) {
            success |= battmonitor_set_temperature(temperature, i);
        }
    }
    return success;
}
#endif // AP_TEMPERATURE_SENSOR_ENABLED

// return true if cycle count can be provided and fills in cycles argument
bool battmonitor_get_cycle_count(uint8_t instance, uint16_t *cycles)
{
    if (instance >= MB_BATT_MONITOR_MAX_INSTANCES || (batt_monitor.drivers[instance] == NULL)) {
        return false;
    }
    return battmonitor_backend_get_cycle_count(batt_monitor.drivers[instance], cycles);
}

bool battmonitor_arming_checks(size_t buflen, char *buffer)
{
    char temp_buffer[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1] = {0};

    for (uint8_t i = 0; i < MB_BATT_MONITOR_MAX_INSTANCES; i++) {
        if (batt_monitor.drivers[i] != NULL && !(battmonitor_backend_arming_checks(batt_monitor.drivers[i], temp_buffer, sizeof(temp_buffer)))) {
            rt_snprintf(buffer, buflen, "Battery %d %s", i + 1, temp_buffer);
            return false;
        }
    }

    return true;
}

/*
  reset battery remaining percentage for batteries that integrate to
  calculate percentage remaining
*/
bool battmonitor_reset_remaining_mask(uint16_t battery_mask, float percentage)
{
    bool ret = true;
    enum BattMonitorFailsafe highest_failsafe = BattMonitorFailsafeNone;
    for (uint8_t i = 0; i < batt_monitor._num_instances; i++) {
        if ((1U<<i) & battery_mask) {
            if (batt_monitor.drivers[i] != NULL) {
                ret &= battmonitor_backend_reset_remaining(batt_monitor.drivers[i], percentage);
            } else {
                ret = false;
            }
        }
        if (batt_monitor.state[i].failsafe > highest_failsafe) {
            highest_failsafe = batt_monitor.state[i].failsafe;
        }
    }

    // If all backends are not in failsafe then set overall failsafe state
    if (highest_failsafe == BattMonitorFailsafeNone) {
        batt_monitor._highest_failsafe_priority = INT8_MAX;
        batt_monitor._has_triggered_failsafe = false;
        // and reset notify flag
        notify_flags.failsafe_battery = false;
    }
    return ret;
}

// Returns the mavlink charge state. The following mavlink charge states are not used
// MAV_BATTERY_CHARGE_STATE_EMERGENCY , MAV_BATTERY_CHARGE_STATE_FAILED
// MAV_BATTERY_CHARGE_STATE_UNHEALTHY, MAV_BATTERY_CHARGE_STATE_CHARGING
int8_t battmonitor_get_mavlink_charge_state(const uint8_t instance)
{
    if (instance >= batt_monitor._num_instances) {
        return (int8_t)MAV_BATTERY_CHARGE_STATE_UNDEFINED;
    }

    switch (batt_monitor.state[instance].failsafe) {

    case BattMonitorFailsafeNone:
        if (battmonitor_get_mavlink_fault_bitmask(instance) != 0 || !battmonitor_healthy()) {
            return (int8_t)MAV_BATTERY_CHARGE_STATE_UNHEALTHY;
        }
        return (int8_t)MAV_BATTERY_CHARGE_STATE_OK;

    case BattMonitorFailsafeLow:
        return (int8_t)MAV_BATTERY_CHARGE_STATE_LOW;

    case BattMonitorFailsafeCritical:
        return (int8_t)MAV_BATTERY_CHARGE_STATE_CRITICAL;
    }

    // Should not reach this
    return (int8_t)MAV_BATTERY_CHARGE_STATE_UNDEFINED;
}

// Returns mavlink fault state
uint32_t battmonitor_get_mavlink_fault_bitmask(const uint8_t instance)
{
    if (batt_monitor.drivers[instance] == NULL) {
        return 0;
    }
    return battmonitor_backend_get_mavlink_fault_bitmask(batt_monitor.drivers[instance]);
}

/// returns true if a battery failsafe has ever been triggered
bool battmonitor_has_failsafed(void)
{
    return batt_monitor._has_triggered_failsafe;
};

/// returns the highest failsafe action that has been triggered
int8_t battmonitor_get_highest_failsafe_priority(void)
{
    return batt_monitor._highest_failsafe_priority;
};

// get battery resistance estimate in ohms
float battmonitor_get_resistance()
{
    return battmonitor_get_resistance2(MB_BATT_PRIMARY_INSTANCE);
}

float battmonitor_get_resistance2(uint8_t instance)
{
    return batt_monitor.state[instance].resistance;
}


// healthy - returns true if monitor is functioning
static void battmonitor_publish_state(uint8_t instance)
{
    battery_state.timestamp_us = time_micros64();

    battery_state.type                         = batt_monitor._params[instance]._type;
    battery_state.has_cell_voltages            = battmonitor_has_cell_voltages(instance);
    rt_memcpy(battery_state.cell_voltages, &batt_monitor.state[instance].cell_voltages, MIN(sizeof(battery_state.cell_voltages), sizeof(batt_monitor.state[instance].cell_voltages)));
    battery_state.voltage                      = batt_monitor.state[instance].voltage;
    battery_state.current_amps                 = batt_monitor.state[instance].current_amps;
    battery_state.consumed_mah                 = batt_monitor.state[instance].consumed_mah;
    battery_state.consumed_wh                  = batt_monitor.state[instance].consumed_wh;
    battery_state.last_time_micros             = batt_monitor.state[instance].last_time_micros;
    battery_state.low_voltage_start_ms         = batt_monitor.state[instance].low_voltage_start_ms;
    battery_state.critical_voltage_start_ms    = batt_monitor.state[instance].critical_voltage_start_ms;
    battery_state.temperature_use              = battmonitor_get_temperature(&battery_state.temperature, instance);
#if MB_TEMPERATURE_SENSOR_ENABLED
    battery_state.temperature_external_use     = batt_monitor.state[instance].temperature_external_use;
    battery_state.temperature_external         = batt_monitor.state[instance].temperature_external;
#endif
    battery_state.temperature_time             = batt_monitor.state[instance].temperature_time;
    battery_state.voltage_resting_estimate     = batt_monitor.state[instance].voltage_resting_estimate;
    battery_state.resistance                   = batt_monitor.state[instance].resistance;
    battery_state.failsafe                     = batt_monitor.state[instance].failsafe;
    battery_state.healthy                      = batt_monitor.state[instance].healthy;
    battery_state.is_powering_off              = batt_monitor.state[instance].is_powering_off;
    battery_state.powerOffNotified             = batt_monitor.state[instance].powerOffNotified;
    battery_state.time_remaining               = batt_monitor.state[instance].time_remaining;
    battery_state.has_time_remaining           = batt_monitor.state[instance].has_time_remaining;
    battery_state.charge_state                 = battmonitor_get_mavlink_charge_state(instance);
    battery_state.gcs_voltage                  = battmonitor_gcs_voltage(instance);
    battery_state.has_current                  = batt_monitor.drivers[instance] != NULL ? battmonitor_backend_has_current(batt_monitor.drivers[instance]) : false;
    battery_state.has_consumed_energy          = batt_monitor.drivers[instance] != NULL ? battmonitor_backend_has_consumed_energy(batt_monitor.drivers[instance]) : false;

    uint8_t remaining_pct;
    if (battmonitor_capacity_remaining_pct(&remaining_pct, instance)) {
        battery_state.capacity_remaining_pct   = (int8_t)remaining_pct;
    } else {
        battery_state.capacity_remaining_pct   = -1;
    }

    switch (instance) {
        case 0:
            itc_publish(ITC_ID(battery_state), &battery_state);
            break;

        case 1:
            itc_publish(ITC_ID(battery2_state), &battery_state);
            break;

        case 2:
            itc_publish(ITC_ID(battery3_state), &battery_state);
            break;

        default:
            break;
    }
}

// healthy - returns true if monitor is functioning
static void battmonitor_publish_status()
{
    if (batt_monitor._num_instances == 0) {
        return;
    }

    battery_status.timestamp_us             = time_micros64();
    battery_status.primary_voltage          = batt_monitor.state[MB_BATT_PRIMARY_INSTANCE].voltage;
    battery_status.primary_gcs_voltage      = battmonitor_gcs_voltage(MB_BATT_PRIMARY_INSTANCE);
    battery_status.primary_current_amps     = batt_monitor.state[MB_BATT_PRIMARY_INSTANCE].current_amps;
    battery_status.primary_consumed_mah     = batt_monitor.state[MB_BATT_PRIMARY_INSTANCE].consumed_mah;
    battery_status.primary_consumed_wh      = batt_monitor.state[MB_BATT_PRIMARY_INSTANCE].consumed_wh;
    battery_status.max_instances            = MB_BATT_MONITOR_MAX_INSTANCES;
    battery_status.num_instances            = batt_monitor._num_instances;
    battery_status.primary_instances        = MB_BATT_PRIMARY_INSTANCE;
    battery_status.cells_max                = MB_BATT_MONITOR_CELLS_MAX;
    battery_status.all_healthy              = battmonitor_healthy();
    battery_status.primary_healthy          = battmonitor_healthy2(MB_BATT_PRIMARY_INSTANCE);
    battery_status.has_failsafed            = batt_monitor._has_triggered_failsafe;
    battery_status.primary_has_current      = batt_monitor.drivers[MB_BATT_PRIMARY_INSTANCE] != NULL ? battmonitor_backend_has_current(batt_monitor.drivers[MB_BATT_PRIMARY_INSTANCE]) : false;
    battery_status.primary_has_consumed_energy = batt_monitor.drivers[MB_BATT_PRIMARY_INSTANCE] != NULL ? battmonitor_backend_has_consumed_energy(batt_monitor.drivers[MB_BATT_PRIMARY_INSTANCE]) : false;

    uint8_t remaining_pct;
    if (battmonitor_capacity_remaining_pct(&remaining_pct, MB_BATT_PRIMARY_INSTANCE)) {
        battery_status.primary_remaining_pct   = (int8_t)remaining_pct;
    } else {
        battery_status.primary_remaining_pct   = -1;
    }

    itc_publish(ITC_ID(battery_status), &battery_status);
}
/*------------------------------------test------------------------------------*/


